package cmlabs.simpleciphers;


import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;

/**
 * Created by IntelliJ IDEA.
 * User: jte
 * Date: 9.4.11
 * Time: 19.17
 * To change this template use File | Settings | File Templates.
 */
public class SubstitutionCipher {

    public SubstitutionCipher(){

        this.T = 65536;
        permutation  = randomShuffle(T);
        reversePermutation = new int [T];
        for (int i = 0; i < permutation.length; ++i)
            reversePermutation[permutation[i]] = i;
    }

    public SubstitutionCipher(int[] cipher){
        if (!checkCorrectness(cipher.clone())){
            throw new IllegalArgumentException("Incorrect cipher!");
        }
        permutation = Arrays.copyOf(cipher, cipher.length);
        for (int i = 0; i < permutation.length; ++i)
            reversePermutation[permutation[i]] = i;
        this.T = cipher.length;
    }

    public String encrypt(String value){
        if (value.length() == 0){
            throw new IllegalArgumentException("Empty encryption argument");
        }
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < value.length(); ++i){
            result.append((char)permutation[value.charAt(i) % T]);
        }
        return result.toString();
    }

    public String decrypt(String value){
        if (value.length() == 0){
            throw new IllegalArgumentException("Empty encryption argument");
        }
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < value.length(); ++i){
            result.append((char)reversePermutation[value.charAt(i) % T]);
        }
        return result.toString();
    }

    private boolean checkCorrectness(int []toDo){
        if (toDo.length != 65536){
            throw new IllegalArgumentException("Must be positive period");
        }
        int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
        for (int cur: toDo){
            min = Math.min(min, cur);
            max = Math.max(max, cur);
        }
        if (max >= toDo.length || min < 0){
            return false;
        }
        HashSet<Integer> elements = new HashSet<Integer>();
        for (int cur: toDo){
            elements.add(cur);
        }
        return elements.size() == toDo.length;

    }

    private int[] randomShuffle(int N){
        int []result = new int [N];
        for (int i = 0 ; i < N; ++i){
            result[i] = i;
        }
        Random generator = new Random(System.currentTimeMillis());
        for (int i = result.length; i > 1; --i){
            int j = generator.nextInt(i);
            int t = result[i - 1];
            result[i - 1] = j;
            result[j] = t;
        }
        return result;
    }

    private int[] permutation;
    private int[] reversePermutation;
    private int T;
}
